package application.services;

import domain.model.project.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author Gregory Boissinot
 */
public class ApplicationServiceImpl implements ApplicationService {

    private Logger logger = LoggerFactory.getLogger(ApplicationServiceImpl.class);

    private final ProjectRepository projectRepository;

    private final CIService ciService;

    public ApplicationServiceImpl(ProjectRepository projectRepository, CIService ciService) {
        this.projectRepository = projectRepository;
        this.ciService = ciService;
    }

    @Override
    public List<Project> getAllProjectInfo() {
        return projectRepository.findAll();
    }

    @Override
    public Project getProjectInfo(ProjectId projectId) {
        return projectRepository.findById(projectId);
    }

    @Override
    public void importProject(Project project, boolean withCIServer) {

        final ProjectId projectId = project.getProjectId();
        if (projectRepository.isProjectExist(projectId)) {
            logger.info(String.format("Project %s already imported.", projectId));
            return;
        }

        projectRepository.store(project);
        if (withCIServer) {
            ciService.createOrUpdateCIJob(project);
        }
        logger.debug("{} successfully imported", projectId);
    }

    @Override
    public void updateProject(ProjectId projectId, Project newProject, boolean updateCIServer) {

        if (!projectRepository.isProjectExist(projectId)) {
            logger.info("{} does not exist", projectId);
            throw new NoProjectException(String.format("No existing %s project.", projectId));
        }

        final ProjectId newProjectProjectId = newProject.getProjectId();
        if (!projectRepository.isProjectExist(newProjectProjectId)) {
            throw new RuntimeException(String.format("No existing %s for the future updated project.", newProjectProjectId));
        }
        if (!projectId.equals(newProjectProjectId)) {
            throw new RuntimeException(String.format("Trying to update an another project."));
        }

        projectRepository.update(projectId, newProject);
        if (updateCIServer) {
            syncCIJob(newProject);
        }
    }

    @Override
    public void openProject(ProjectId projectId, boolean withCIServer) {
        Project project = projectRepository.findById(projectId);
        Project updatedProject = project.copyWithNewStatus(ProjectStatus.OPEN);
        projectRepository.update(project.getProjectId(), updatedProject);
        if (withCIServer) {
            syncCIJob(updatedProject);
        }
        logger.debug("{} successfully opened", projectId);
    }

    @Override
    public void closeProject(ProjectId projectId, boolean withCIServer) {
        Project project = projectRepository.findById(projectId);
        Project updatedProject = project.copyWithNewStatus(ProjectStatus.CLOSED);
        projectRepository.update(project.getProjectId(), updatedProject);
        if (withCIServer) {
            syncCIJob(updatedProject);
        }
        logger.debug("{} successfully closed", projectId);
    }

    @Override
    public void updateSCM(ProjectId projectId, ProjectSCM projectSCM, boolean withCIServer) {
        Project project = projectRepository.findById(projectId);
        Project updatedProject = project.copyWithNewProjectSCM(projectSCM);
        projectRepository.update(projectId, updatedProject);
        if (withCIServer) {
            syncCIJob(updatedProject);
        }
        logger.debug("SCM url of {} successfully updated", projectId);
    }

    @Override
    public void updateDevelopers(ProjectId projectId, List<ProjectDeveloper> projectDeveloperList, boolean withCIServer) {
        if (!projectRepository.isProjectExist(projectId)) {
            logger.info("{} does not exist", projectId);
            throw new NoProjectException(String.format("No existing %s project.", projectId));
        }
        Project project = projectRepository.findById(projectId);
        Project updatedProject = project.copyWithNewProjectDevelopers(projectDeveloperList);
        projectRepository.update(projectId, updatedProject);
        if (withCIServer) {
            syncCIJob(updatedProject);
        }
        logger.debug("developers of {} successfully updated", projectId);
    }

    @Override
    public void syncCIJobs() {
        List<Project> allProjects = projectRepository.findAll();
        for (Project project : allProjects) {
            syncCIJob(project);
        }
        logger.debug("sync CI jobs done");
    }

    private void syncCIJob(Project project) {
        if (project.getStatus().isOpen()) {
            ciService.createOrUpdateCIJob(project);
        } else if (project.getStatus().isClose()) {
            ciService.deleteCIJob(project);
        }
        logger.debug("sync CI job of {} done", project.getProjectId());
    }

    @Override
    public Set<String> findAllScmConnectionURL() {
        Set<String> scmUrlSet = new HashSet<String>();
        List<Project> projects = projectRepository.findAll();
        for (Project project : projects) {
            ProjectSCM projectSCM = project.getProjectSCM();
            if (projectSCM != null) {
                if (projectSCM instanceof ProjectSCM) {
                    scmUrlSet.add(((ProjectSCM) projectSCM).getScmConnection());
                }
            }
        }
        logger.debug("found {} scm urls", scmUrlSet.size());
        return Collections.unmodifiableSet(scmUrlSet);
    }

}
